package test.client.thread;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;

import test.client.support.balancer.LoadBalancer;
import test.domain.environment.node.Server;

public abstract class ClientThread extends BaseThread implements ChatManagerListener, MessageListener {
	protected XMPPConnection connection;
	private Server server;
	
	private String username;
	private String password;
		
	private boolean loggedin = false;
			
	@Override
	public abstract void run();
	
	protected void login() throws Exception {
		if (username == null || password == null) 
			throw new IllegalStateException("username or password is null.");
		if (server == null) 
			throw new IllegalStateException("server is null.");
		
		SmackConfiguration.setPacketReplyTimeout(10000);
		
		// Configure the connection
		ConnectionConfiguration config; 
		
		config = new ConnectionConfiguration(
					server.getDomain(), server.getPort(), server.getServiceName());		

		config.setSendPresence(true);
		config.setSASLAuthenticationEnabled(true);
		
		connection = new XMPPConnection(config);
		
		// Register packet listener
		PacketListener listener = new PacketListener() {
			@Override
			public void processPacket(Packet packet) {
				if (packet instanceof Message) {
					// Let MessageListener process the message packet
					return;
				}
				else if (packet instanceof Presence) {
					processPresence((Presence) packet);
					return;
				}
				else if (packet instanceof IQ) {
					processIQ((IQ) packet);
				}
				else {
					log.debug(username + ": packet type: " + packet.toString());
				}
			}
		};
		
		log.debug(this.getUsername() + ": Register a packet listener.");
		this.connection.addPacketListener(listener, null);
				
		connection.connect();
		connection.login(username, password);
		this.loggedin = true;
		
		// Register message listener
		log.debug(this.username + ": Register a message listener.");
		connection.getChatManager().addChatListener(this);		
	}
	
	protected void processIQ(IQ iq) {
//		log.debug(packet.toXML());
	}
	
	protected void sendPresence(Presence.Type type, String status) {
		Presence presence = new Presence(type);
		presence.setStatus(status);
		this.connection.sendPacket(presence);
	}
	
	protected void processPresence(Presence presence) {
		log.info(presence.toXML());
	}
	
	protected void sendMessage(String message, String to) throws XMPPException {
		Chat chat = connection.getChatManager().createChat(to, null);
		log.debug("chat instance: " + chat);
		this.sendMessage(chat, message);
	}
	
	protected void sendMessage(Chat chat, String message) throws XMPPException {
		if (chat == null) throw new IllegalArgumentException("Chat instance is null.");
		chat.sendMessage(message);
	}
	
	protected void processMessage(Message message) {
		log.debug("incoming message: " + message.getBody());
	}
	
	@Override
	public void processMessage(Chat chat, Message message) {
		if (message.getType() == Message.Type.chat) {
			log.debug(chat.getParticipant() + " says: " + message.getBody());
		}
		else {
			log.debug("Packet : " + message.getType() + ", " + message.getBody());
		}
	}
	
	@Override
	public void chatCreated(Chat chat, boolean createdLocally) {
		log.debug("chat created.");
		if (!createdLocally)
			chat.addMessageListener(this);
	}
	
	protected void disconnect() {
		if (loggedin) connection.disconnect();
	}
	
	public Server getServer() {
		return server;
	}
	public void setServer(Server server) {
		this.server = server;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}
